// UpdateMaker.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
#include <commctrl.h>
#include <Commdlg.h>
#include <crtdbg.h>
#include <shlobj.h>
#include <Wininet.h>
#include "UpdateMaker.h"
#include "../revision.h"
#include "../XMLParser/xmlParser.h"
#include "../client/File.h"
#include "../client/Util.h"
#include "../client/ZUtils.h"
#include "../client/BZUtils.h"
#include "../client/ThrottleManager.h"
#include "../client/FilteredFile.h"
#include <openssl/rsa.h>
#include <openssl/sha.h>
#include <openssl/pem.h>
#include "../ViewFeatures/ViewlinkDCKey.h"

#define MAX_LOADSTRING 100
#define MAX_ARGS_COMMAND_LINE 7

// Global Variables:
HINSTANCE hInst;								// current instance
TCHAR szTitle[MAX_LOADSTRING];					// The title bar text
TCHAR szWindowClass[MAX_LOADSTRING];			// the main window class name

// Forward declarations of functions included in this code module:
ATOM				MyRegisterClass(HINSTANCE hInstance);
BOOL				InitInstance(HINSTANCE, int);
LRESULT CALLBACK	WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK	About(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK	UpdateDialogProc(HWND, UINT, WPARAM, LPARAM);
BOOL OnInitUpdateDialog(HWND hDlg);
VOID StartUpdateCreation(HWND hDlg);
void SelectFolder(int id_Edit, HWND hDlg);
bool UsingModule(HWND hDlg, const string & titleName);
size_t WriteDataAsFile(byte* indata, size_t inSize, const wstring& path, const wstring& packName, const wstring& woutFolder, int packerType);
void CreateXMLUpdateFile(const string& outXMLPath, const string& outData);
void CreateNewKeyPair(HWND hDlg);
wstring getFileName(HWND hDlg, wstring dialogTitle);
BOOL SignFile(wstring & outFileW, wstring& signFile, HWND hDlg);
BOOL VerifySignFile(wstring & outFileW, wstring& signFile, HWND hDlg);
bool ProcessCMDLine(wstring& cmdLine);
void RemoveLeadEndCommas(wstring& stringWC);

//[+]PPA
static bool is_wine()
{
	HMODULE module = GetModuleHandleA("ntdll.dll");
	if (!module)
		return false;
	return (GetProcAddress(module, "wine_server_call") != NULL);
}

bool g_isWineCheck = false;


std::wstring sourceFolder = L".\\";
std::wstring destinFolder = L".\\update\\";
std::wstring secretKeyPath = L".\\secret\\developer.key";
std::wstring downloadURL = L"http://viewlinkdc-update.googlecode.com/svn/trunk/3xx/beta/";
std::wstring descrPath = L".\\readme_changelog_lite.rtf";
std::wstring version = L_REVISION_NUM_STR;
bool isBeta = true;

bool isCMDSet = false;

int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{

	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);
    HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);

	g_isWineCheck  = is_wine();

	// InitCommonControlsEx() is required on Windows XP if an application
	// manifest specifies use of ComCtl32.dll version 6 or later to enable
	// visual styles.  Otherwise, any window creation will fail.

	INITCOMMONCONTROLSEX InitCtrls;
	InitCtrls.dwSize = sizeof(InitCtrls);
	// Set this to include all the common control classes you want to use
	// in your application.
	InitCtrls.dwICC = ICC_STANDARD_CLASSES | ICC_PROGRESS_CLASS;
	InitCommonControlsEx(&InitCtrls);


	Util::initialize();
	TimerManager::newInstance();
	ThrottleManager::newInstance();
	ResourceManager::newInstance();
	if (ResourceManager::getInstance()->isRTL())
	{
		SetProcessDefaultLayout(LAYOUT_RTL);
	}
	SettingsManager::newInstance();
	SettingsManager::getInstance()->load();
	SettingsManager::getInstance()->LoadLanguage();// [+] FlylinkDC

	wstring cmdLine = lpCmdLine;
	if (!cmdLine.empty())
	{
		isCMDSet = ProcessCMDLine(cmdLine);
	}

	DialogBox(hInstance, MAKEINTRESOURCE(IDD_UPDATECREATEDLG), NULL, UpdateDialogProc);

	SettingsManager::deleteInstance();// [+] FlylinkDC
	ResourceManager::deleteInstance();
	ThrottleManager::deleteInstance();
	TimerManager::deleteInstance();

	::CoUninitialize();


	return 1;
}

bool ProcessCMDLine(wstring& cmdLine)
{
	//
	// 1st - -S source folder
	// 2nd - -D destination
	// 3rd - -K secretkey
	// 4rd - -U URL
	// 5th - -I Info
	// 6th - -V Version
	// 7th - -B/-R (Aaoa/?aeec)

	wstring arglines[MAX_ARGS_COMMAND_LINE];
	int iCount = 0;
	bool isCommas = false;
	for (UINT i = 0; i<cmdLine.length();i++)
	{
		if (cmdLine.c_str()[i] == L' ' && !isCommas)
		{
			iCount++;
			if (iCount == MAX_ARGS_COMMAND_LINE)
				break;
		}else
		{
			if (cmdLine.c_str()[i] == L'"')
				isCommas = !isCommas;
			arglines[iCount]+= cmdLine.c_str()[i];
		}
	}
	// Process arglines;
	for (UINT i = 0; i< MAX_ARGS_COMMAND_LINE; i++)
	{
		if (arglines[i].length() >= 2 && arglines[i].c_str()[0] == L'-' )
		{
			switch(arglines[i].c_str()[1])
			{
			case L'S':
				{
					sourceFolder = arglines[i].substr(2, arglines[i].length() - 2);
					RemoveLeadEndCommas(sourceFolder);
				}break;
			case L'D':
				{
					destinFolder = arglines[i].substr(2, arglines[i].length() - 2);
					RemoveLeadEndCommas(destinFolder);
				}break;
			case L'K':
				{
					secretKeyPath = arglines[i].substr(2, arglines[i].length() - 2);
					RemoveLeadEndCommas(secretKeyPath);
				}break;
			case L'U':
				{
					downloadURL = arglines[i].substr(2, arglines[i].length() - 2);
					RemoveLeadEndCommas(downloadURL);
				}break;
			case L'I':
				{
					descrPath = arglines[i].substr(2, arglines[i].length() - 2);
					RemoveLeadEndCommas(descrPath);
				}break;
			case L'V':
				{
					version = arglines[i].substr(2, arglines[i].length() - 2);
					RemoveLeadEndCommas(version);
				}break;
			case L'B':
				{
					isBeta = true;
				}break;
			case L'R':
				{
					isBeta = false;
				}break;
			}
		}
	}

	return true;
}


void RemoveLeadEndCommas(wstring& stringWC)
{
	if (stringWC.c_str()[0] == L'"' && stringWC.c_str()[stringWC.length()-1] == L'"' )
		stringWC = stringWC.substr(1, stringWC.length()-2);
}

//
//  FUNCTION: MyRegisterClass()
//
//  PURPOSE: Registers the window class.
//
//  COMMENTS:
//
//    This function and its usage are only necessary if you want this code
//    to be compatible with Win32 systems prior to the 'RegisterClassEx'
//    function that was added to Windows 95. It is important to call this function
//    so that the application will get 'well formed' small icons associated
//    with it.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX);

	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= LoadIcon(hInstance, MAKEINTRESOURCE(IDI_UPDATEMAKER));
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= MAKEINTRESOURCE(IDC_UPDATEMAKER);
	wcex.lpszClassName	= szWindowClass;
	wcex.hIconSm		= LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

	return RegisterClassEx(&wcex);
}

//
//   FUNCTION: InitInstance(HINSTANCE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;

   hInst = hInstance; // Store instance handle in our global variable

   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

//
//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Processes messages for the main window.
//
//  WM_COMMAND	- process the application menu
//  WM_PAINT	- Paint the main window
//  WM_DESTROY	- post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	PAINTSTRUCT ps;
	HDC hdc;

	switch (message)
	{
	case WM_COMMAND:
		wmId    = LOWORD(wParam);
		wmEvent = HIWORD(wParam);
		// Parse the menu selections:
		switch (wmId)
		{
		case IDM_ABOUT:
			DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
			break;
		case IDM_EXIT:
			DestroyWindow(hWnd);
			break;
		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
		}
		break;
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		// TODO: Add any drawing code here...
		EndPaint(hWnd, &ps);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(lParam);
	switch (message)
	{
	case WM_INITDIALOG:
		return (INT_PTR)TRUE;

	case WM_COMMAND:
		if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
		{
			EndDialog(hDlg, LOWORD(wParam));
			return (INT_PTR)TRUE;
		}
		break;
	}
	return (INT_PTR)FALSE;
}


INT_PTR CALLBACK UpdateDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	BOOL mesageProccessed = FALSE;

	INITCOMMONCONTROLSEX InitCtrlEx;

	InitCtrlEx.dwSize = sizeof(INITCOMMONCONTROLSEX);
	InitCtrlEx.dwICC  = ICC_PROGRESS_CLASS;
	InitCommonControlsEx(&InitCtrlEx);

	UNREFERENCED_PARAMETER(lParam);
	switch (message)
	{
	case WM_INITDIALOG:
		{
			if (!OnInitUpdateDialog(hDlg))
			{
				EndDialog(hDlg, -1);
			}
			mesageProccessed = TRUE;
			break;
		}

	case WM_COMMAND:
		if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
		{
			EndDialog(hDlg, LOWORD(wParam));
			mesageProccessed = TRUE;
		}else if (LOWORD(wParam) == IDC_CREATEUPDATE)
			{
				StartUpdateCreation(hDlg);
				mesageProccessed = TRUE;
			}else if (LOWORD(wParam) == IDC_BTNSELFOLDERUPDATE)
				{
					SelectFolder(IDC_EDITFOLDERTOCREATE, hDlg);
					mesageProccessed = TRUE;
				}else if (LOWORD(wParam) == IDC_BTNSELFOLDERUPDATE2)
				{
					SelectFolder(IDC_EDITOUTPUTFOLDER, hDlg);
					mesageProccessed = TRUE;
				}else if (LOWORD(wParam) == IDC_CREATEKEYS)
				{
					CreateNewKeyPair(hDlg);
					mesageProccessed = TRUE;
				}
		break;
	}
	return (INT_PTR)mesageProccessed;
}


BOOL OnInitUpdateDialog(HWND hDlg)
{
	CheckDlgButton(hDlg, IDC_EXE, BST_CHECKED);
	CheckDlgButton(hDlg, IDC_LANG, BST_CHECKED);
	CheckDlgButton(hDlg, IDC_UTILITIES, BST_CHECKED);
	CheckDlgButton(hDlg, IDC_PORTALBROWSER, BST_CHECKED);
	CheckDlgButton(hDlg, IDC_WEBSERVER, BST_CHECKED);
	CheckDlgButton(hDlg, IDC_EMOPACKS, BST_CHECKED);
	CheckDlgButton(hDlg, IDC_SOUNDS, BST_CHECKED);
	CheckDlgButton(hDlg, IDC_ICONTHEMES, BST_CHECKED);
	CheckDlgButton(hDlg, IDC_COLORTHEMES, BST_CHECKED);
	// CheckDlgButton(hDlg, IDC_CHATBOTS, BST_CHECKED);
	CheckDlgButton(hDlg, IDC_DOCUMENTAION, BST_CHECKED);

	SendMessage(GetDlgItem(hDlg, IDC_RELEASEBETA), CB_RESETCONTENT, 0, 0); 
    SendMessage(GetDlgItem(hDlg, IDC_RELEASEBETA), CB_ADDSTRING, 0, (LPARAM)L"Beta"); 
    SendMessage(GetDlgItem(hDlg, IDC_RELEASEBETA), CB_ADDSTRING, 0, (LPARAM)L"Release"); 

	SendMessage(GetDlgItem(hDlg, IDC_PACKER), CB_RESETCONTENT, 0, 0); 
    SendMessage(GetDlgItem(hDlg, IDC_PACKER), CB_ADDSTRING, 0, (LPARAM)L"Unpacked"); 
    SendMessage(GetDlgItem(hDlg, IDC_PACKER), CB_ADDSTRING, 0, (LPARAM)L"BZip2"); 
    SendMessage(GetDlgItem(hDlg, IDC_PACKER), CB_ADDSTRING, 0, (LPARAM)L"Zip"); 
	SendMessage(GetDlgItem(hDlg, IDC_PACKER), CB_SETCURSEL, (WPARAM)1, 0); 

	//AutoArray <wchar_t> currentDir(MAX_PATH);
	//GetCurrentDirectory(MAX_PATH-1, currentDir.get());
	//wstring sourceUpdateFolder = currentDir;
	//AppendPathSeparator(sourceUpdateFolder);
	//wstring destinationUpdateFolder = sourceUpdateFolder+L"..\\update\\";


	//wstring sourceFolder = L".\\";
	//wstring destinFolder = L".\\update\\";
	//wstring secretKeyPath = L".\\secret\\developer.key";
	//wstring downloadURL = L"http://viewlinkdc-update.googlecode.com/svn/trunk/3xx/beta/";
	//wstring descrPath = L".\\readme_changelog_lite.rtf";
	//bool isBeta = true;

	AppendPathSeparator(sourceFolder);
	AppendPathSeparator(destinFolder);

	SendMessage(GetDlgItem(hDlg, IDC_RELEASEBETA), CB_SETCURSEL, (WPARAM)isBeta?0:1, 0); 
	SetDlgItemText(hDlg, IDC_EDITFOLDERTOCREATE, sourceFolder.c_str());
	SetDlgItemText(hDlg, IDC_EDITOUTPUTFOLDER, destinFolder.c_str());
	SetDlgItemText(hDlg, IDC_PRIVATEKEY, secretKeyPath.c_str());
	SetDlgItemText(hDlg, IDC_DOWNLOADURL, downloadURL.c_str());
	SetDlgItemText(hDlg,IDC_DESCRIPTIONRTF, descrPath.c_str());

	SetDlgItemText(hDlg, IDC_VERSION, version.c_str());

	if (isCMDSet)
		PostMessage(hDlg, WM_COMMAND, IDC_CREATEUPDATE, 0);

	return 1;
}


int CALLBACK 
MyBrowseCallbackProc( HWND hwnd,UINT uMsg, LPARAM, LPARAM lpData )
{
    if ( uMsg == BFFM_INITIALIZED ) {
        if ( lpData ) { // forgot to set it?
            ::SendMessage( hwnd,BFFM_SETSELECTION,TRUE, lpData );
        }
    }
    return 0;
}


void SelectFolder(int id_Edit, HWND hDlg )
{
	// Show Select Folder Dlg and set it to id_EDIT
	wchar_t selectedFolder[MAX_PATH];
	GetDlgItemText(hDlg, id_Edit, selectedFolder, 255);

	BROWSEINFO bri;
	bri.hwndOwner = hDlg;
	bri.pidlRoot = NULL;
	bri.pszDisplayName = selectedFolder;
	bri.lpszTitle = L"Select folder";
	bri.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE | BIF_NONEWFOLDERBUTTON ;
	bri.lpfn = MyBrowseCallbackProc;
	bri.lParam = (DWORD)selectedFolder;


	LPITEMIDLIST pidl =  SHBrowseForFolder(&bri);
	if ( pidl != 0 )
	{
		// get the name of the folder
		TCHAR path[MAX_PATH];
		if ( SHGetPathFromIDList ( pidl, path ) )
		{
			SetDlgItemText(hDlg, id_Edit, path);
		}

		// free memory used
		IMalloc * imalloc = 0;
		if ( SUCCEEDED( SHGetMalloc ( &imalloc )) )
		{
			imalloc->Free ( pidl );
			imalloc->Release ( );
		}
	}

}


bool UsingModule(HWND hDlg, const wstring & titleName)
{
	if (titleName == L"exe")
	{
		return IsDlgButtonChecked(hDlg, IDC_EXE) == BST_CHECKED;
	}else if (titleName == L"utilities")
	{
		return IsDlgButtonChecked(hDlg, IDC_UTILITIES) == BST_CHECKED;
	}else if (titleName == L"lang")
	{
		return IsDlgButtonChecked(hDlg, IDC_LANG) == BST_CHECKED;
	}else if (titleName == L"portalbrowser")
	{
		return IsDlgButtonChecked(hDlg, IDC_PORTALBROWSER) == BST_CHECKED;
	}else if (titleName == L"emopacks")
	{
		return IsDlgButtonChecked(hDlg, IDC_EMOPACKS) == BST_CHECKED;
	}else if (titleName == L"webserver")
	{
		return IsDlgButtonChecked(hDlg, IDC_WEBSERVER) == BST_CHECKED;
	}else if (titleName == L"sounds")
	{
		return IsDlgButtonChecked(hDlg, IDC_SOUNDS) == BST_CHECKED;
	}else if (titleName == L"iconthemes")
	{
		return IsDlgButtonChecked(hDlg, IDC_ICONTHEMES) == BST_CHECKED;
	}else if (titleName == L"colorthemes")
	{
		return IsDlgButtonChecked(hDlg, IDC_COLORTHEMES) == BST_CHECKED;
	}else if (titleName == L"documentation")
	{
		return IsDlgButtonChecked(hDlg, IDC_DOCUMENTAION) == BST_CHECKED;
	}
	return false;
}

void AppendBackPathSeparator(wstring& p_path) //[+]PPA
{
	if (p_path.length())
		if (p_path[ p_path.length() - 1 ] != '/')
			p_path += '/';
}

wstring ReplaceAllBackSlashWithDiv(wstring& p_path)
{
	wstring outString = Util::emptyStringW;
	for (unsigned int i = 0 ; i<p_path.length(); i++)
	{
		if (p_path[ i ] == PATH_SEPARATOR)
			outString += L"/";
		else
			outString += p_path[ i ];
	}
	return outString;
}

void StartUpdateCreation(HWND hDlg)
{
	int packerType = SendMessage(GetDlgItem(hDlg, IDC_PACKER), CB_GETCURSEL, (WPARAM)0, 0); 
	if (packerType == CB_ERR)
		return;

	int releaseBeta = SendMessage(GetDlgItem(hDlg, IDC_RELEASEBETA), CB_GETCURSEL, (WPARAM)0, 0); 
	if (releaseBeta  == CB_ERR)
		return;

	wchar_t urlW[MAX_PATH];
	GetDlgItemText(hDlg, IDC_DOWNLOADURL, urlW, MAX_PATH-1);
	wstring wurl = urlW;
	AppendBackPathSeparator(wurl);	

	wchar_t outFolderW[MAX_PATH];
	GetDlgItemText(hDlg, IDC_EDITOUTPUTFOLDER, outFolderW, MAX_PATH-1);
	wstring woutFolder = outFolderW;
	AppendPathSeparator(woutFolder);		

	wstring outXMLPath = woutFolder;
	if (releaseBeta == 0)
		outXMLPath+=L"Update5_beta.xml";
	else if (releaseBeta == 1)
		outXMLPath+=L"Update5.xml";

	wstring outSignPath = woutFolder;
	if (releaseBeta == 0)
		outSignPath+=L"Update5_beta.sign";
	else if (releaseBeta == 1)
		outSignPath+=L"Update5.sign";

	wstring outDescrPath = woutFolder;
	if (releaseBeta == 0)
		outDescrPath+=L"Update5_beta.rtf";
	else if (releaseBeta == 1)
		outDescrPath+=L"Update5.rtf";

	wchar_t versionW[256];
	GetDlgItemText(hDlg, IDC_VERSION, versionW, 255);
	wstring version = versionW;
	// Time
	SYSTEMTIME pTime;
	GetSystemTime(&pTime);
	wchar_t timeW[256];
	InternetTimeFromSystemTime(&pTime, INTERNET_RFC1123_FORMAT, timeW, 255);
	wstring wtime = timeW;


	wchar_t pathW[MAX_PATH];
	GetDlgItemText(hDlg, IDC_EDITFOLDERTOCREATE, pathW, MAX_PATH-1);
	wstring wpath = pathW;
	AppendPathSeparator(wpath);		
	string path = Text::wideToAcp(wpath);
	path+="UpdateSource5.xml";

	wchar_t pathD[MAX_PATH];
	GetDlgItemText(hDlg, IDC_DESCRIPTIONRTF, pathD, MAX_PATH-1);
	wstring wpathD = pathD;


	size_t totalFileFound = 0;
	try{
		File f(path, File::READ, File::OPEN, false);
		string data = f.read();
		f.close();
		XMLParser::XMLResults xRes;
		const wstring wstr = Text::acpToWide(data);
		XMLParser::XMLNode xRootNode = XMLParser::XMLNode::parseString(wstr.c_str(), 0, &xRes);
		AutoUpdateModules modules;
		Updater updater;
		if (xRes.error == XMLParser::eXMLErrorNone)
		{
			XMLParser::XMLNode updateNode = xRootNode.getChildNode(L"Update5");
			if (!updateNode.isEmpty())
			{
				XMLParser::XMLNode updaterNode = updateNode.getChildNode(L"Updater");
				if (!updaterNode.isEmpty())
				{
					int k = 0;
					XMLParser::XMLNode fileNode = updaterNode.getChildNode(L"File", &k);
					while (!fileNode.isEmpty())
					{
						AutoUpdateFile updateFile;
						// Find files, get TTH, get Size, Pack'em, get PackedSize, put to output folder, and make record in Update5_??? .xml
						updateFile.m_sName = fileNode.getAttributeOrDefault(L"Name");
						updateFile.m_sPath = fileNode.getAttributeOrDefault(L"Path");
						if (!updateFile.m_sPath.empty())
							AppendPathSeparator(updateFile.m_sPath);
						updateFile.m_absPath = wpath+updateFile.m_sPath;
						updateFile.m_absPath += updateFile.m_sName;
						wstring fileSys = fileNode.getAttributeOrDefault(L"Sys");
						if (fileSys.empty())
							updateFile.m_sys = AutoUpdateFile::xALL;
						else if (fileSys == L"x86")
							updateFile.m_sys = AutoUpdateFile::x86;
						else if (fileSys == L"x64")
							updateFile.m_sys = AutoUpdateFile::x64;
						else 
							updateFile.m_sys = AutoUpdateFile::OsUnknown;

						updateFile.m_sPackName = updateFile.m_sName;
						if ( packerType == 1)
						{
							updateFile.m_sPackName+=L".bz2";
							updateFile.m_packer = AutoUpdateFile::BZip2;
						}
						else if (packerType == 2)
						{
							updateFile.m_sPackName+=L".zip";
							updateFile.m_packer = AutoUpdateFile::Zip;
						}else
						{
							updateFile.m_packer  = AutoUpdateFile::Unpacked;
						}
						updateFile.m_sDownloadURL = wurl+ReplaceAllBackSlashWithDiv(updateFile.m_sPath)+updateFile.m_sPackName;
						// Search file
						FileFindIter end;
						bool bRet = false;
						string filePath = Text::wideToAcp(updateFile.m_absPath);
						FileFindIter iFile(filePath);
						if (iFile != end)
						{
							updateFile.m_size = iFile->getSize();
							updater.m_Files.push_back(updateFile);
						}
						fileNode = updaterNode.getChildNode(L"File", &k);
					}
				}

				int i = 0;
				XMLParser::XMLNode moduleNode = updateNode.getChildNode(L"Module", &i);
				while (!moduleNode.isEmpty())
				{
					if (moduleNode.isAttributeSet(L"Title"))
					{
						wstring titleName = moduleNode.getAttribute(L"Title") ; 
						if ( UsingModule(hDlg, titleName))
						{
							AutoUpdateModule module;
							module.m_sName = titleName;
							int j = 0;
							XMLParser::XMLNode fileNode = moduleNode.getChildNode(L"File", &j);
							while (!fileNode.isEmpty())
							{
								AutoUpdateFile updateFile;
								// Find files, get TTH, get Size, Pack'em, get PackedSize, put to output folder, and make record in Update5_??? .xml
								updateFile.m_sName = fileNode.getAttributeOrDefault(L"Name");
								updateFile.m_sPath = fileNode.getAttributeOrDefault(L"Path");
								if (!updateFile.m_sPath.empty())
									AppendPathSeparator(updateFile.m_sPath);
								updateFile.m_absPath = wpath+updateFile.m_sPath;
								updateFile.m_absPath += updateFile.m_sName;
								wstring fileSys = fileNode.getAttributeOrDefault(L"Sys");
								if (fileSys.empty())
									updateFile.m_sys = AutoUpdateFile::xALL;
								else if (fileSys == L"x86")
									updateFile.m_sys = AutoUpdateFile::x86;
								else if (fileSys == L"x64")
									updateFile.m_sys = AutoUpdateFile::x64;
								else 
									updateFile.m_sys = AutoUpdateFile::OsUnknown;

								updateFile.m_sPackName = updateFile.m_sName;
								if ( packerType == 1)
								{
									updateFile.m_sPackName+=L".bz2";
									updateFile.m_packer = AutoUpdateFile::BZip2;
								}
								else if (packerType == 2)
								{
									updateFile.m_sPackName+=L".zip";
									updateFile.m_packer = AutoUpdateFile::Zip;
								}else
								{
									updateFile.m_packer  = AutoUpdateFile::Unpacked;
								}
								updateFile.m_sDownloadURL = wurl+ReplaceAllBackSlashWithDiv(updateFile.m_sPath)+updateFile.m_sPackName;
								// Search file
								FileFindIter end;
								bool bRet = false;
								string filePath = Text::wideToAcp(updateFile.m_absPath);
								FileFindIter iFile(filePath);
								if (iFile != end)
								{
									updateFile.m_size = iFile->getSize();
									module.m_Files.push_back(updateFile);
									totalFileFound++;
								}
								fileNode = moduleNode.getChildNode(L"File", &j);
							}
							modules.push_back(module);
						}
					}
					moduleNode = updateNode.getChildNode(L"Module", &i);
				}
			}
		}
		if (totalFileFound > 0)
		{
			unsigned int filesProcessed = 0;
			SendMessage(GetDlgItem(hDlg, IDC_PROGRESSUPDATE), PBM_SETRANGE32, (WPARAM)0, (LPARAM)totalFileFound);
			// Write output xml;
			XMLParser::XMLNode updateNode = XMLParser::XMLNode::createXMLTopNode(L"xml",TRUE);  
			// version="1.0" encoding="utf-8"?>
			updateNode.addAttribute(L"version",L"1.0");  
			updateNode.addAttribute(L"encoding",L"utf-8");  
			XMLParser::XMLNode update5Node = updateNode.addChild(L"Update5");
			update5Node.addAttribute(L"xmlns:xsi",L"http://www.w3.org/2001/XMLSchema-instance");  
			update5Node.addAttribute(L"Version",version.c_str());  
			update5Node.addAttribute(L"UpdateDate", wtime.c_str());
			// Add Updaters
			if (updater.m_Files.size())
			{
				XMLParser::XMLNode moduleNode = update5Node.addChild(L"Updater");
				for (unsigned int j = 0; j<updater.m_Files.size(); j++)
				{
					AutoUpdateFile updateFile = updater.m_Files[j];
					// Calculate TTH
					boost::shared_ptr<TigerTree> tth;
					if ( Util::getTTH_MD5(Text::wideToAcp(updateFile.m_absPath), 4096, &tth, NULL, false) )
					{
						updateFile.m_sTTH = Text::acpToWide(tth.get()->getRoot().toBase32());
						// Start Packing....

						size_t inSize = updateFile.m_size;
						size_t outSize = updateFile.m_size;
						boost::shared_array<byte> indata = boost::shared_array<byte>(new byte[updateFile.m_size]);
						File f(Text::wideToAcp(updateFile.m_absPath), File::READ, File::OPEN, false);
						f.read(indata.get(), inSize);
						f.close();

						updateFile.m_packedSize = WriteDataAsFile(indata.get(), inSize, updateFile.m_sPath, updateFile.m_sPackName, woutFolder, packerType);

						XMLParser::XMLNode fileNode = moduleNode.addChild(L"File");
						fileNode.addAttribute(L"Name", updateFile.m_sName.c_str());
						fileNode.addAttribute(L"Path", updateFile.m_sPath.c_str());
						if (updateFile.m_sys == AutoUpdateFile::x86)
							fileNode.addAttribute(L"Sys", L"x86");
						else if (updateFile.m_sys == AutoUpdateFile::x64)
							fileNode.addAttribute(L"Sys", L"x64");

						if (updateFile.m_packer == AutoUpdateFile::BZip2)
							fileNode.addAttribute(L"Pack", L"bz2");
						else if (updateFile.m_packer == AutoUpdateFile::Zip)
							fileNode.addAttribute(L"Pack", L"zip");
						fileNode.addAttribute(L"Size", Util::toStringW( updateFile.m_size).c_str());
						fileNode.addAttribute(L"TTH", updateFile.m_sTTH.c_str());
						fileNode.addAttribute(L"PackedSize",  Util::toStringW( updateFile.m_packedSize).c_str());
						fileNode.addAttribute(L"Url",  updateFile.m_sDownloadURL.c_str());
					}
				}

			}

			for (unsigned int i = 0; i<modules.size(); i++)
			{
				XMLParser::XMLNode moduleNode = update5Node.addChild(L"Module");
				moduleNode.addAttribute(L"Title", modules[i].m_sName.c_str());
				for (unsigned int j = 0; j<modules[i].m_Files.size(); j++)
				{
					AutoUpdateFile updateFile = modules[i].m_Files[j];
					// Calculate TTH
					boost::shared_ptr<TigerTree> tth;
					if ( Util::getTTH_MD5(Text::wideToAcp(updateFile.m_absPath), 4096, &tth, NULL, false) )
					{
						updateFile.m_sTTH = Text::acpToWide(tth.get()->getRoot().toBase32());
						// Start Packing....

						size_t inSize = updateFile.m_size;
						size_t outSize = updateFile.m_size;
						boost::shared_array<byte> indata = boost::shared_array<byte>(new byte[updateFile.m_size]);
						File f(Text::wideToAcp(updateFile.m_absPath), File::READ, File::OPEN, false);
						f.read(indata.get(), inSize);
						f.close();

						updateFile.m_packedSize = WriteDataAsFile(indata.get(), inSize, updateFile.m_sPath, updateFile.m_sPackName, woutFolder, packerType);
						
						XMLParser::XMLNode fileNode = moduleNode.addChild(L"File");
						fileNode.addAttribute(L"Name", updateFile.m_sName.c_str());
						fileNode.addAttribute(L"Path", updateFile.m_sPath.c_str());
						if (updateFile.m_sys == AutoUpdateFile::x86)
							fileNode.addAttribute(L"Sys", L"x86");
						else if (updateFile.m_sys == AutoUpdateFile::x64)
							fileNode.addAttribute(L"Sys", L"x64");

						if (updateFile.m_packer == AutoUpdateFile::BZip2)
							fileNode.addAttribute(L"Pack", L"bz2");
						else if (updateFile.m_packer == AutoUpdateFile::Zip)
							fileNode.addAttribute(L"Pack", L"zip");
						fileNode.addAttribute(L"Size", Util::toStringW( updateFile.m_size).c_str());
						fileNode.addAttribute(L"TTH", updateFile.m_sTTH.c_str());
						fileNode.addAttribute(L"PackedSize",  Util::toStringW( updateFile.m_packedSize).c_str());
						fileNode.addAttribute(L"Url",  updateFile.m_sDownloadURL.c_str());
					}
					if (filesProcessed < totalFileFound)
						SendMessage(GetDlgItem(hDlg, IDC_PROGRESSUPDATE), PBM_SETPOS, (WPARAM)filesProcessed++, (LPARAM)0);
				}
			}

			// Save XML file
			// [!] SSA - only utf-16 update5Node.writeToFile(outXMLPath.c_str(), "utf-8");
			int isize = 0;
			wchar_t* outFileW = update5Node.createXMLString(1, &isize);
			if (isize > 0)
			{
				string outFile = Text::wideToUtf8(outFileW);
				CreateXMLUpdateFile(Text::wideToAcp(outXMLPath), outFile);
				free(outFileW);
			}
			// Save Descritpion file
			// wpathD => outDescrPath
			if (!::CopyFile(wpathD.c_str(), outDescrPath.c_str(), FALSE))
			{
				MessageBox(hDlg, L"Description File copy error", L"ViewlinkDC++ update maker", MB_OK);
				return;
			}
			if (SignFile(outXMLPath, outSignPath, hDlg) && VerifySignFile(outXMLPath, outSignPath, hDlg))
			{
				//MessageBox(hDlg, L"Update successfully created and Verified", L"ViewlinkDC++ update maker", MB_OK);
				SendMessage(hDlg, WM_CLOSE, 0, 0); //[+]PPA
			}else
			{
				MessageBox(hDlg, L"Sign error", L"ViewlinkDC++ update maker", MB_OK);
			}
		}
	}
	catch(const FileException& e)
	{
		// File Exception error
		MessageBox(hDlg, Text::toT(e.getError() + " [Update failed - File error]").c_str() , L"ViewlinkDC++ update maker", MB_OK);
	}
	catch(const Exception& e)
	{
		// Zip/Bzip exceptions
		MessageBox(hDlg, Text::toT(e.getError() + " [Update failed - Packer error]").c_str(), L"ViewlinkDC++ update maker", MB_OK);
	}

}
void CreateXMLUpdateFile(const string& outXMLPath, const string& outData)
{
	File xmlFile(outXMLPath, File::WRITE,  File::TRUNCATE | File::CREATE, false);
	// Write header
	//unsigned char h[3] = {0xEF, 0xBB, 0xBF};
	//xmlFile.write(h, 3);
	string header = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
	xmlFile.write(header);
	xmlFile.write(outData);
	xmlFile.close();
}

size_t WriteDataAsFile(byte* indata, size_t inSize, const wstring& path, const wstring& packName, const wstring& woutFolder, int packerType)
{
	// Create Folders
	wstring wdestinationPath = woutFolder + path + packName;
	size_t pos = wdestinationPath.find(L'\\');
	while (pos != wstring::npos)
	{
		wstring folder = wdestinationPath.substr(0, pos);					
		CreateDirectory(folder.c_str(), NULL);
		pos = wdestinationPath.find(L'\\', pos+1);
	}

	size_t outSize = 0;
	boost::shared_ptr<OutputStream> outFilePtr = boost::shared_ptr<OutputStream>(new File(Text::wideToAcp(wdestinationPath), File::WRITE, File::TRUNCATE | File::CREATE, false));
	if (packerType == 0)
	{
		// File f( Text::wideToAcp(wdestinationPath), File::WRITE, File::OPEN| File::CREATE, false);
		outFilePtr.get()->write( indata, inSize);
		outFilePtr.get()->flush();
		outSize = inSize;
	}
	else if (packerType == 1) // bz2
	{
		FilteredOutputStream<BZFilter, false> outFile(outFilePtr.get());
		outSize += outFile.write(indata, inSize);
		outSize += outFile.flush();

	}else if (packerType == 2) // zip
	{
		FilteredOutputStream<ZFilter, false> outFile(outFilePtr.get());
		outSize += outFile.write(indata, inSize);
		outSize += outFile.flush();
	}

	return outSize;

}


static void callback(int stage, int count, void* arg)
{

}

const BIGNUM *BN_value_RSA_F4(void)
{
	static const BN_ULONG data_one = RSA_F4;
	static const BIGNUM const_one = { (BN_ULONG *) &data_one, 1,1,0,BN_FLG_STATIC_DATA };
	return (&const_one);
}

void CreateNewKeyPair(HWND hDlg)
{
	RSA *rsa = NULL;
	BN_GENCB cb;
	int lenPub = 0;
	int lenPriv = 0;
	byte publicKey[2048], *pubKey;
	byte privateKey[2048], *privKey;
	do {
		rsa = RSA_new();
		if (!rsa)
			break;

		BN_GENCB_set_old(&cb, callback, (char*) stdout);


		if (!RSA_generate_key_ex(rsa, 1024, (BIGNUM*)BN_value_RSA_F4(), &cb))
			break;

		pubKey = publicKey;
		privKey = privateKey;

		lenPub = i2d_RSAPublicKey(rsa, &pubKey);
		lenPriv = i2d_RSAPrivateKey(rsa, &privKey);
		
	}while(0);

	if (rsa)
		RSA_free(rsa);
	rsa = NULL;

	// Save Data
	if (lenPub && lenPriv)
	{
		wstring wPrivFN = getFileName(hDlg, L"Save Private Key");
		if (wPrivFN.empty())
			return;
		boost::shared_ptr<OutputStream> outFilePriv = boost::shared_ptr<OutputStream>(new File(Text::wideToAcp(wPrivFN), File::WRITE, File::TRUNCATE | File::CREATE, false));
		outFilePriv->write(privateKey, lenPriv);
		outFilePriv->flush();

		wstring wPubFN = getFileName(hDlg, L"Save Public Key");
		if (wPubFN.empty())
			return;
		boost::shared_ptr<OutputStream> outFilePub = boost::shared_ptr<OutputStream>(new File(Text::wideToAcp(wPubFN), File::WRITE, File::TRUNCATE | File::CREATE, false));
		outFilePub->write(publicKey, lenPub);
		outFilePub->flush();

		SetWindowText(GetDlgItem(hDlg, IDC_PRIVATEKEY), wPrivFN.c_str());
	}
}

wstring getFileName(HWND hDlg, wstring dialogTitle)
{
	wchar_t szFilter[] = L"Key files\0*.key\0All Files\0*.*\0\0";
	wchar_t szFile[MAX_PATH];
	szFile[0] = 0;
	wchar_t initFolder[MAX_PATH];
	initFolder[0] = 0;
	wchar_t defExt[] = L"key";

	OPENFILENAME Ofn;
	memset(&Ofn,0,sizeof(Ofn));    
	Ofn.lStructSize = sizeof(OPENFILENAME); 
	Ofn.hwndOwner = hDlg; 
	Ofn.lpstrFilter = szFilter; 
	Ofn.lpstrFile= szFile; 
	Ofn.nMaxFile = _countof(szFile); 
	Ofn.lpstrInitialDir = NULL; 
	Ofn.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT; 
	Ofn.lpstrTitle = dialogTitle.c_str(); 
	Ofn.lpstrDefExt = defExt;

	if (GetSaveFileName(&Ofn))
	{
		return Ofn.lpstrFile;
	}
	return L"";
}


BOOL SignFile(wstring & outFileW, wstring& signFile, HWND hDlg)
{
	try{
		// Read All Data from outFileW;
		File xmlFile( Text::wideToUtf8( outFileW ), File::READ,  File::OPEN, false);
		string allData = xmlFile.read();
		xmlFile.close();

		wchar_t privateKeyFile[MAX_PATH];
		GetDlgItemText(hDlg, IDC_PRIVATEKEY, privateKeyFile, MAX_PATH-1);
		wstring wPrivFN = privateKeyFile;
		if (wPrivFN.empty())
			return FALSE;

		File keyFile( Text::wideToUtf8( wPrivFN ), File::READ,  File::OPEN, false);
		int64_t sizeData = keyFile.getSize();
		boost::shared_array<byte> keyData = boost::shared_array<byte>(new byte[sizeData]);
		keyFile.read( keyData.get(), (size_t&) sizeData);
		keyFile.close();

		RSA* r = NULL; 
		boost::shared_array<byte> sig;
		unsigned int sig_len = 0;
		do
		{
			// Make SHA hash
			int rc = -1;
			SHA_CTX sha_ctx = { 0 };
			byte digest[SHA_DIGEST_LENGTH];

			rc = SHA1_Init(&sha_ctx);
			if (rc != 1)
				break;
			rc = SHA1_Update(&sha_ctx, allData.c_str(), allData.length());
			if (rc != 1)
				break;
			rc = SHA1_Final(digest, &sha_ctx);
			if (rc != 1)
				break;

			// Extract Key
			// b = BIO_new_mem_buf(keyData.get(), (int) sizeData);
			// r = PEM_read_bio_RSAPrivateKey(b, NULL, NULL, NULL);
			const byte* p = keyData.get();
			r = d2i_RSAPrivateKey(NULL, &p, sizeData);

			// sign hash
			sig = boost::shared_array<byte>(new byte[RSA_size(r)]);
			
			rc = RSA_sign(NID_sha1, digest, sizeof(digest), sig.get(), &sig_len, r);
			if (rc != 1)
				break;


		}while (false);
		if ( r ) RSA_free(r); r = NULL;

		if (sig_len > 0)
		{
			// Write sign into file
			File outSig( Text::wideToUtf8( signFile ), File::WRITE, File::TRUNCATE | File::CREATE, false);
			outSig.write( sig.get(), sig_len);
			outSig.close();
			return TRUE;
		}
	}
	catch(const FileException&)
	{

	}
	return FALSE;
}

BOOL VerifySignFile(wstring & outFileW, wstring& signFile, HWND hDlg)
{
	try{
		// Read All Data from outFileW;
		File xmlFile( Text::wideToUtf8( outFileW ), File::READ,  File::OPEN, false);
		string allData = xmlFile.read();
		xmlFile.close();

		boost::shared_array<byte> sig;
		File sigFile( Text::wideToUtf8( signFile) , File::READ, File::OPEN, false);
		unsigned int sig_len = sigFile.getSize();
		sig = boost::shared_array<byte>(new byte[sig_len]);
		sigFile.read( sig.get(), sig_len );
		sigFile.close();


		RSA* r = NULL; 
		int rc = -1;
		do
		{
			// Make SHA hash
			SHA_CTX sha_ctx = { 0 };
			byte digest[SHA_DIGEST_LENGTH];

			rc = SHA1_Init(&sha_ctx);
			if (rc != 1)
				break;
			rc = SHA1_Update(&sha_ctx, allData.c_str(), allData.length());
			if (rc != 1)
				break;
			rc = SHA1_Final(digest, &sha_ctx);
			if (rc != 1)
				break;

			// Extract Key
			const byte* p = sViewlinkDCPublicKey;
			r = d2i_RSAPublicKey(NULL, &p, sizeof(sViewlinkDCPublicKey));

			
			rc = RSA_verify(NID_sha1, digest, sizeof(digest), sig.get(), sig_len, r);
			if (rc != 1)
				break;


		}while (false);
		if ( r ) RSA_free(r); r = NULL;

		if (rc != 1)
		{
				MessageBox(hDlg, L"Verify failed", L"ViewlinkDC++ update maker", MB_OK);
		}else
		{
				return TRUE;
		}
	}
	catch(const FileException&)
	{

	}
	return FALSE;
}